﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;

using DotSpatial.Data;
using DotSpatial.Topology;

using CSA.Lib;

namespace CSA.OptimalMonitoringNetwork
{
    public class CSOMNProblem : ICSOptimizationProblem
    {               
        #region Fields

        Hashtable m_Paras = new Hashtable();
        Hashtable m_CustomOPerators = new Hashtable();   
        CSAgorithmsHelper m_AgorithmsHelper = new CSAgorithmsHelper();        
        int m_NewStationCount = 2;

        OMNOutputConfigUI m_CustomOutputConfigUI = new OMNOutputConfigUI();
        OMNCustomEncodeUI m_CustomEncodeUI = new OMNCustomEncodeUI();
        OMNCustomEvaluationUI m_CustomEvaluationUI = new OMNCustomEvaluationUI();    

        string m_CandidateStationFile = "";
        string m_ExistingStationFile = "";
        string m_OptimalLocationsFile = "";

        DotSpatial.Data.IFeatureSet m_CandidateStations = null;
        Int32 m_CandidateStationsCount = 0;
        List<DotSpatial.Topology.Coordinate> m_ExistingStations = new List<Coordinate>();

        bool m_ProblemInitialized = false;

        double m_AffCoe = 0.0;

        #endregion

        public CSOMNProblem()
        {
            String nErrorStr = "";
            InitialProblem(ref nErrorStr);
        }

        ~CSOMNProblem()
        { }

        public bool InitialProblem(ref String nErrorStr)
        {  
            return true;
        }

        public bool Initialization(ref String nErrorStr)
        {
            try
            {
                if (m_ProblemInitialized == true)
                {
                    return true;
                }

                if (m_ExistingStationFile != "" && System.IO.File.Exists(m_ExistingStationFile) &&
                    m_CandidateStationFile != "" && System.IO.File.Exists(m_CandidateStationFile))
                {
                    ShapefileDataProvider nSource = new ShapefileDataProvider();
                    m_CandidateStations = nSource.Open(m_CandidateStationFile);
                    IFeatureSet pFeatureset = nSource.Open(m_ExistingStationFile);
                    m_ExistingStations.Clear();
                    for (int i = 0; i < pFeatureset.Features.Count; i++)
                    {                     
                        DotSpatial.Topology.Point pBasicGeo = (DotSpatial.Topology.Point)pFeatureset.Features[i].BasicGeometry;
                        DotSpatial.Topology.Coordinate nCurStation = new Coordinate(pBasicGeo.X, pBasicGeo.Y);
                        m_ExistingStations.Add(nCurStation);
                    }
                    m_CandidateStationsCount = m_CandidateStations.Features.Count;
                    m_ProblemInitialized = true;
                }
                else
                {
                    nErrorStr = "File does not exist.";
                    return false;
                }
                return true;
            }
            catch(Exception ex)
            {
                nErrorStr = ex.Message;
                return false;
            }
        }

        public String ExistingStationFile
        {
            get { return m_ExistingStationFile; }
            set 
            {
                m_ExistingStationFile = value;
                m_ProblemInitialized = false;  
            }
        }

        public String CandidateStationFile
        {
            get { return m_CandidateStationFile; }
            set { m_CandidateStationFile = value; }
        }

        public String OptimalLocationsFile
        {
            get { return m_OptimalLocationsFile; }
            set { m_OptimalLocationsFile = value; }
        }

        public List<CSObjectiveInfo> Objectives
        {
            get 
            {
                List<CSObjectiveInfo> nObjectives = new List<CSObjectiveInfo>();           
                CSObjectiveInfo nMMSDObjective = new CSObjectiveInfo();
                nMMSDObjective.Name = "MMSD";
                nMMSDObjective.Caption = "Mean of the Shortest Distances";
                nMMSDObjective.IsCostObjecitve = true;
                nMMSDObjective.Description = "Minimization of the Mean of the Shortest Distances";
                nObjectives.Add(nMMSDObjective);            
                return nObjectives;
            }
        }

        public string Name
        {
            get { return "OptimalMonitoringNetwork"; }
        }

        public string GeneDataType
        { get { return "Int32"; } }

        public string Caption { get { return global::CSA.OptimalMonitoringNetwork.TextString.OMNProblemName; } }

        public string Description 
        {
            get 
            {
                return global::CSA.OptimalMonitoringNetwork.TextString.OMNProblemDes ; 
            } 
        }

        public String ClassName { get { return "CSOMNProblem"; } }

        public String LibraryName { get { return "CSA.OptimalMonitoringNetwork"; } }

        public Hashtable Inputs 
        {
            get 
            {
                Hashtable nInputs = new Hashtable();
                nInputs.Add("CandidateStationFile", m_CandidateStationFile);
                nInputs.Add("ExistingStationFile", m_ExistingStationFile);
                return nInputs; 
            } 
        }

        public Hashtable Outputs 
        { 
            get            
            {
                Hashtable nOutputs = new Hashtable();
                nOutputs.Add("OptimalLocationsFile", m_OptimalLocationsFile);
                return nOutputs;
            } 
        }

        public Hashtable CustomOperators
        {
            get { return m_CustomOPerators; }
        }

        public CSCustomUI GetCustomEncodeUI()
        {
            return m_CustomEncodeUI;
        }

        public CSCustomUI GetCustomObjectFuntionUI()
        {
            return m_CustomEvaluationUI;
        }

        public CSCustomUI GetCustomOutputConfigUI()
        {
            return m_CustomOutputConfigUI;
        }

        public CSMOProcessor MultiObjectiveProcessor
        {
            get { return null; }
            set { ;}
        }

        public CSAntibody CreateAb()
        {
            if (m_CandidateStations != null)
            {
                CSOMNAntibody nNewAb = new CSOMNAntibody();
                //nNewAb.AddGene(203);
                //nNewAb.AddGene(1965);
                //nNewAb.AddGene(2244);
                //nNewAb.AddGene(2196);
            
                for (int i = 0; i < m_NewStationCount; i++)
                {
                    int nCurPos=m_AgorithmsHelper.NextRandomNumber(0, m_CandidateStations.Features.Count - 1);                    
                    nNewAb.AddGene(nCurPos);
                }          
                return nNewAb;
            }
            else
            {
                return null;
            }
        }

        public double EvaluateAb(ref CSAntibody Antibody)
        {
            if (m_CandidateStations != null && Antibody != null && Antibody is CSOMNAntibody)
            {
                //Calculate the Mean of the Shortest Distances.
                List<Int32> nGenes = (List<Int32>)Antibody.Genes;
                List<DotSpatial.Topology.Coordinate> nSelectedStations = new List<Coordinate>(m_ExistingStations);
                for (int i = 0; i < nGenes.Count; i++)
                {
                    DotSpatial.Topology.Point nCurStation = (DotSpatial.Topology.Point)m_CandidateStations.Features[nGenes[i]].BasicGeometry;
                    nSelectedStations.Add(nCurStation.Coordinate);
                }
                Int32 nTotalStations = nSelectedStations.Count;
                AI.Core.DataStatisticResult nStatistics = new AI.Core.DataStatisticResult("MMSD");
                Double nSum = 0.0;
                for (int i = 0; i < m_CandidateStations.Features.Count; i++)
                {
                    DotSpatial.Topology.Point nCurStation = (DotSpatial.Topology.Point)m_CandidateStations.Features[i].BasicGeometry;
                    //Find the nearest Station.
                    Double nMinDis = -1.0;
                    Int32 nNearestID = 0;
                    for (int j = 0; j < nSelectedStations.Count; j++)
                    {
                        Double nCurDis = Math.Pow((nCurStation.X - nSelectedStations[j].X), 2) + Math.Pow((nCurStation.Y - nSelectedStations[j].Y), 2);
                        nCurDis = Math.Sqrt(nCurDis);
                        if (nMinDis < 0 && nCurDis > 0.0)
                        {
                            nMinDis = nCurDis;
                            nNearestID = j;
                        }
                        else if (nCurDis > 0)
                        {
                            if (nCurDis < nMinDis)
                            {
                                nMinDis = nCurDis;
                                nNearestID = j;
                            }
                        }
                    }
                    nSum += nMinDis;
                }
                nSum = nSum / m_CandidateStationsCount;
                Antibody.ObjectiveValues.Clear();
                Antibody.ObjectiveValues.Add(nSum);
                if (m_AffCoe == 0.0)
                {
                    m_AffCoe = nSum * 2;
                }
                Antibody.Affinity = m_AffCoe - nSum;
                return Antibody.Affinity;
            }
            else
            {
                return -1;
            } 
        }

        public bool MutateAb(ref CSAntibody Antibody, Double nMuatorRate)
        {
            CSOMNAntibody nNewAb = (CSOMNAntibody)Antibody;         
            Int32 nIt;
            Int32 nSize = (Int32)nNewAb.AntibodyLength;         
            for (nIt = 0; nIt < nSize; ++nIt)
            {
                Double nRnd = m_AgorithmsHelper.NextRandomNumber(0.0, 1.0);
                if (nRnd < nMuatorRate)
                {
                    int nPos = m_AgorithmsHelper.NextRandomNumber(0, m_CandidateStationsCount - 1);
                    List<Int32> nGenes = (List<Int32>)nNewAb.Genes;
                    bool nContinue = true;
                    while (nContinue)
                    {
                        if (!nGenes.Contains(nPos))
                        {
                            break;
                        }
                        nPos = m_AgorithmsHelper.NextRandomNumber(0, m_CandidateStationsCount - 1);
                    }
                    nNewAb.SetGene(nPos, nIt); 
                }
            }       
            return true;
        }

        public bool CrossoverAb(ref CSAntibody Antibody1, ref CSAntibody Antibody2, List<bool> nCrossTemplate)
        {
            bool nCross = false;
            for (int i = 0; i < nCrossTemplate.Count; i++)
            {
                if (nCross==false && nCrossTemplate[i])
                {
                    nCross = true;
                    Int32 nPos1 = (Int32)Antibody1.GetGene(i);
                    Int32 nPos2 = (Int32)Antibody2.GetGene(i);
                    Antibody1.SetGene(nPos2, i);
                    Antibody2.SetGene(nPos1, i);
                }
                else if (nCross == true && nCrossTemplate[i]==false)
                {
                    Int32 nPos1 = (Int32)Antibody1.GetGene(i);
                    Int32 nPos2 = (Int32)Antibody2.GetGene(i);
                    Antibody1.SetGene(nPos2, i);
                    Antibody2.SetGene(nPos1, i);
                }
                else if (nCross == false && nCrossTemplate[i])
                {
                    nCross = false;
                }
            }
            return true;
        }         

        public bool WriteOptimalResult(List<CSAntibody> BestAntibody)
        {
            if (BestAntibody != null && BestAntibody.Count > 0)
            {
                CheckResultFile();
                ShapefileDataProvider nSource = new ShapefileDataProvider();
                IFeatureSet pFeatureSet = nSource.Open(m_OptimalLocationsFile);
                for (int i = 0; i < BestAntibody[0].AntibodyLength; i++)
                {
                    Int32 nNewStationID = (Int32)BestAntibody[0].GetGene(i);
                    IFeature pFeature= m_CandidateStations.GetFeature(nNewStationID);
                    DotSpatial.Topology.Point nPoint = (DotSpatial.Topology.Point)pFeature.BasicGeometry;
                    IFeature pLocationFeature = pFeatureSet.AddFeature(nPoint);
                    pLocationFeature.DataRow["LocationID"] = pFeature.Fid.ToString();
                    if (i % 100 == 0)
                    {
                        pFeatureSet.Save();
                    }
                }
                pFeatureSet.Save();
                pFeatureSet.Close();
            }
            return true;
        }

        private void CheckResultFile()
        {
            try
            {
                DotSpatial.Data.IFeatureSet pFeatureSet = null;
                if (m_OptimalLocationsFile.Trim() != "" && System.IO.File.Exists(m_OptimalLocationsFile))
                {
                    ShapefileDataProvider nSource = new ShapefileDataProvider();
                    IFeatureSet pTempFetaureset = nSource.Open(m_OptimalLocationsFile);
                    bool nNeedDelete = false;
                    if (pTempFetaureset.FeatureType != DotSpatial.Topology.FeatureType.Point)
                    {
                        nNeedDelete = true;
                    }
                    else if (!pTempFetaureset.DataTable.Columns.Contains("LocationID"))
                    {
                        nNeedDelete = true;
                    }
                    if (nNeedDelete)
                    {
                        try
                        {
                            System.IO.File.Delete(m_OptimalLocationsFile);
                            pFeatureSet = new FeatureSet(DotSpatial.Topology.FeatureType.Point);
                            pFeatureSet.ProjectionString = m_CandidateStations.ProjectionString;
                            pFeatureSet.DataTable.Columns.Add("LocationID", typeof(string));
                            pFeatureSet.SaveAs(m_OptimalLocationsFile, true);
                            pFeatureSet.Close();
                        }
                        catch
                        {
                            return;
                        }
                    }
                    else
                    {
                        pFeatureSet = pTempFetaureset;
                        pFeatureSet.IndexMode = false;
                        for (int i = pFeatureSet.Features.Count - 1; i > -1; --i)
                        {
                            IFeature pCurFeature = pFeatureSet.GetFeature(i);
                            pFeatureSet.Features.Remove(pCurFeature);
                        }
                        pFeatureSet.DataTable.Clear();
                        pFeatureSet.DataTable.AcceptChanges();
                        pFeatureSet.Save();
                        pFeatureSet.Close();
                    }
                }
                else
                {
                    pFeatureSet = new FeatureSet(DotSpatial.Topology.FeatureType.Point);
                    pFeatureSet.ProjectionString = m_CandidateStations.ProjectionString;
                    pFeatureSet.DataTable.Columns.Add("LocationID", typeof(string));            
                    pFeatureSet.SaveAs(m_OptimalLocationsFile, true);
                    pFeatureSet.Close();
                }
            }
            catch
            { }
            /*
            try
            {
                DotSpatial.Data.IFeatureSet pFeatureSet = null;
                if (m_OptimalLocationsFile.Trim() != "" && System.IO.File.Exists(m_OptimalLocationsFile))
                {
                    ShapefileDataProvider nSource = new ShapefileDataProvider();
                    IFeatureSet pTempFetaureset = nSource.Open(m_OptimalLocationsFile);
                    bool nNeedDelete = false;
                    if (pTempFetaureset.FeatureType != DotSpatial.Topology.FeatureType.Point)
                    {
                        nNeedDelete = true;
                    }                   
                    if (nNeedDelete)
                    {
                        try
                        {
                            System.IO.File.Delete(m_OptimalLocationsFile);
                            pFeatureSet = new FeatureSet(DotSpatial.Topology.FeatureType.Point);                        
                            pFeatureSet.SaveAs(m_OptimalLocationsFile, true);
                        }
                        catch
                        {
                            return;
                        }
                    }
                    else
                    {
                        pFeatureSet = pTempFetaureset;
                        //pFeatureSet.IndexMode = false;
                        for (int i = pFeatureSet.Features.Count - 1; i > -1; --i)
                        {
                            IFeature pCurFeature = pFeatureSet.GetFeature(i);
                            pFeatureSet.Features.Remove(pCurFeature);
                        }
                        pFeatureSet.DataTable.Clear();
                        pFeatureSet.DataTable.AcceptChanges();
                        pFeatureSet.Save();
                        pFeatureSet.Close();
                    }
                }
                else
                {
                    pFeatureSet = new FeatureSet(DotSpatial.Topology.FeatureType.Point);                 
                    pFeatureSet.SaveAs(m_OptimalLocationsFile, true);
                    pFeatureSet.Close();
                }
            }
            catch
            { }*/
        }

        public bool CustomOperator(ref CSAntibody Antibody, String nOperatorName)
        {           
            return true;
        }
    
        public int AntibodyLength
        {
            get { return m_NewStationCount; }
        }

        public int NewStationCount
        {
            get { return m_NewStationCount; }
            set { m_NewStationCount = value; }
        }

        public bool ReadEncodeParameters(XmlElement nEncodeNode, ref String nErrorInfo)
        {
            try
            {
                XmlElement nCSFileNode = (XmlElement)nEncodeNode.SelectSingleNode("./CandidateStationFile");
                m_CandidateStationFile = nCSFileNode.GetAttribute("FileName");
                XmlElement nESFileNode = (XmlElement)nEncodeNode.SelectSingleNode("./ExistingStationFile");
                m_ExistingStationFile = nESFileNode.GetAttribute("FileName");
                XmlElement nNewStationNode = (XmlElement)nEncodeNode.SelectSingleNode("./NewStationsCount");
                m_NewStationCount =Convert.ToInt32(nNewStationNode.GetAttribute("value").ToString());      
            }
            catch
            {
                nErrorInfo = "Encode information have not setted corrected";
                return false;
            }
            return true;
        }

        public bool WriteEncodeParameters(XmlElement nEncodeNode, ref String nErrorInfo)
        {
            try
            {
                nEncodeNode.RemoveAll();             

                XmlNode nCandidateStationFileNode = nEncodeNode.OwnerDocument.CreateElement("CandidateStationFile");
                nEncodeNode.AppendChild(nCandidateStationFileNode);
                XmlAttribute nCSFileNameAttribute = nEncodeNode.OwnerDocument.CreateAttribute("FileName");
                nCSFileNameAttribute.Value = m_CandidateStationFile;
                nCandidateStationFileNode.Attributes.Append(nCSFileNameAttribute);   
             
                XmlNode nExistingStationFileNode = nEncodeNode.OwnerDocument.CreateElement("ExistingStationFile");
                nEncodeNode.AppendChild(nExistingStationFileNode);
                XmlAttribute nFileNameAttribute = nEncodeNode.OwnerDocument.CreateAttribute("FileName");
                nFileNameAttribute.Value = m_ExistingStationFile;
                nExistingStationFileNode.Attributes.Append(nFileNameAttribute);

                XmlNode nNewStationsNode = nEncodeNode.OwnerDocument.CreateElement("NewStationsCount");
                nEncodeNode.AppendChild(nNewStationsNode);
                XmlAttribute nNewCountAttribute = nEncodeNode.OwnerDocument.CreateAttribute("value");
                nNewCountAttribute.Value = m_NewStationCount.ToString();
                nNewStationsNode.Attributes.Append(nNewCountAttribute);    

                return true;
            }
            catch(Exception ex)
            {
                nErrorInfo = "Wrtting encode information encounter an error:"+ex.Message;
                return false;
            }
        }

        public bool ValidateConfigure(ref String nErrorStr)
        {
            try
            {
                ShapefileDataProvider nSource = new ShapefileDataProvider();
                IFeatureSet pFeatureSet = null;
                if (m_ExistingStationFile == "" || !System.IO.File.Exists(m_ExistingStationFile))
                {
                    nErrorStr = "please set the Existing Station File.";
                    return false;
                }
                pFeatureSet = nSource.Open(m_ExistingStationFile);
                if (pFeatureSet == null || pFeatureSet.FeatureType != FeatureType.Point)
                {
                    nErrorStr = "You have to select a point file.";
                    return false;
                }               
                if (m_CandidateStationFile == "" || !System.IO.File.Exists(m_CandidateStationFile))
                {
                    nErrorStr = "please set the Candidate Station File.";
                    return false;
                }
                pFeatureSet = nSource.Open(m_CandidateStationFile);
                if (pFeatureSet == null || pFeatureSet.FeatureType != FeatureType.Point)
                {
                    nErrorStr = "You have to select a point file.";
                    return false;
                }
                else
                {
                    if (pFeatureSet.Features.Count < 3)
                    {
                        nErrorStr = "The number of candidate stations must grater than 2.";
                        return false;
                    }
                }
                if (m_OptimalLocationsFile == "" )
                {
                    nErrorStr = "please set the Optimal Locations File.";
                    return false;
                }
                if (m_NewStationCount < 1)
                {
                    nErrorStr = "The number of new stations must grater than 0.";
                    return false;
                }
                return true;
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }           
        }

        public bool ReadEvaluationParameters(XmlElement nEvaluationNode, ref String nErrorInfo)
        {
            return true;
        }

        public bool WriteEvaluationParameters(XmlElement nEvaluationNode, ref String nErrorInfo)
        {
            return true;
        }

        public bool ReadOutputParameters(XmlElement nOutputNode, ref String nErrorInfo)
        {
            try
            {
                XmlElement nFileNode = (XmlElement)nOutputNode.SelectSingleNode("./OptimalLocationsFile");
                m_OptimalLocationsFile = nFileNode.GetAttribute("FileName");              
            }
            catch
            {
                nErrorInfo = "Reading the encode information encounter an error。";
                return false;
            }
            return true;
        }

        public bool WriteOutputParameters(XmlElement nOutputNode, ref String nErrorInfo)
        {
            try
            {
                nOutputNode.RemoveAll();
                XmlNode nFileNode = nOutputNode.OwnerDocument.CreateElement("OptimalLocationsFile");
                nOutputNode.AppendChild(nFileNode);
                XmlAttribute nFileNameAttribute = nOutputNode.OwnerDocument.CreateAttribute("FileName");
                nFileNameAttribute.Value = m_OptimalLocationsFile;
                nFileNode.Attributes.Append(nFileNameAttribute);             
                return true;
            }
            catch (Exception ex)
            {
                nErrorInfo = "Writting the output settings encounter an error :" + ex.Message;
                return false;
            }
        }
    }
}
